import ctypes
import os
import platform
from enum import IntEnum
import numpy as np
from ctypes import *


class DAQ331M:
    class DAQ_Voltage(IntEnum):
        Voltage2V5 = 5  # 2.5V电压量程
        Voltage5V = 6  # 5V电压量程
        Voltage10V = 7  # 10V电压量程
        Voltage15V = 8  # 15V电压量程

    class DAQ_ADC_Channel(IntEnum):
        Ain1 = 0b00000001  # ADC通道1
        Ain2 = 0b00000001 << 1  # ADC通道2
        Ain3 = 0b00000001 << 2  # ADC通道3
        Ain4 = 0b00000001 << 3  # ADC通道4
        Ain5 = 0b00000001 << 4  # ADC通道5
        Ain6 = 0b00000001 << 5  # ADC通道6
        Ain7 = 0b00000001 << 6  # ADC通道7
        Ain8 = 0b00000001 << 7  # ADC通道8
        Ain_All = 0b11111111  # ADC通道1-8全选

    class DAQ_Sample_Rate(IntEnum):
        SampleRate80 = 80  # 80采样率
        SampleRate100 = 100  # 100采样率
        SampleRate500 = 500  # 500采样率
        SampleRate1K = 1000  # 1K采样率
        SampleRate5K = 5000  # 5K采样率
        SampleRate10K = 10000  # 10K采样率
        SampleRate50K = 50000  # 50K采样率
        SampleRate100K = 100000  # 100K采样率

    daq_lib_name: str = "libdaq-2.0.0.dll"
    daq331m_dll_: ctypes.CDLL = None
    daq331m_: ctypes.POINTER(ctypes.c_uint32) = None

    def __init__(self) -> None:
        system = platform.system()
        # 设置相对路径
        base_dir: str = os.path.dirname(os.path.abspath(__file__))
        if system == 'Windows':
            dll_dir: str = os.path.normpath(
                os.path.join(
                    base_dir,
                    '..', '..', '..', '..',
                    'lib', 'Windows', 'libdaq', 'lib',
                    'x64' if os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') else 'x86',
                    'msvc2019'
                )
            )
        elif system == 'Linux':
            self.daq_lib_name = "liblibdaq-2.0.0.so"
            dll_dir: str = os.path.normpath(
                os.path.join(
                    base_dir,
                    '..', '..', '..', '..',
                    'lib', 'Linux', 'libdaq', 'lib',
                    'x86_64'
                )
            )
        elif system == 'Darwin':
            print("no support this system.")
        else:
            print("no support this system.")

        # 检查源目录是否存在
        if not os.path.exists(dll_dir):
            print("Source directory does not exist.")
            return

        # 获取动态库路径
        daq_lib_name: str = self.daq_lib_name
        source_dll_path: str = os.path.join(dll_dir, daq_lib_name)

        # 导入动态库
        self.daq331m_dll_ = CDLL(source_dll_path)

        # 创建DAQ331M
        self.daq331m_dll_.DAQ331M_New.restype = ctypes.POINTER(ctypes.c_uint32)

        # 删除DAQ331M对象
        self.daq331m_dll_.DAQ331M_Delete.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_Delete.restype = ctypes.c_bool

        # 初始化DAQ331M
        self.daq331m_dll_.DAQ331M_InitializeDevice.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_InitializeDevice.restype = ctypes.c_bool

        # 连接DAQ331M
        self.daq331m_dll_.DAQ331M_ConnectedDevice.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_ConnectedDevice.restype = ctypes.c_bool

        # 串口-RS485通信
        self.daq331m_dll_.DAQ331M_UseSerialBackend.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_char_p,
                                                               ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_UseSerialBackend.restype = ctypes.c_bool

        # TCP客户端通信
        self.daq331m_dll_.DAQ331M_UseTCPClientBackend.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_char_p,
                                                                  ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_UseTCPClientBackend.restype = ctypes.c_bool

        # ModbusRTU协议通信
        self.daq331m_dll_.DAQ331M_UseModbusRTUBackend.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_char_p,
                                                                  ctypes.c_uint32, ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_UseModbusRTUBackend.restype = ctypes.c_bool

        # 配置设备ADC参数
        self.daq331m_dll_.DAQ331M_ConfigureADCParameters.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint32,
                                                                     ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_ConfigureADCParameters.restype = ctypes.c_bool

        # 配置ADC通道
        self.daq331m_dll_.DAQ331M_ConfigADCChannel.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_ubyte]
        self.daq331m_dll_.DAQ331M_ConfigADCChannel.restype = ctypes.c_bool

        # 开始采集
        self.daq331m_dll_.DAQ331M_StartCollection.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_StartCollection.restype = ctypes.c_bool

        # 停止采集
        self.daq331m_dll_.DAQ331M_StopCollection.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_StopCollection.restype = ctypes.c_bool

        # 读取数据
        self.daq331m_dll_.DAQ331M_TryReadADCData.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint32,
                                                             ctypes.POINTER(ctypes.c_double), ctypes.c_uint32,
                                                             ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_TryReadADCData.restype = ctypes.c_bool

        # 获取DAQ331M当前使能的通道数量
        self.daq331m_dll_.DAQ331M_GetADCEnableChannelSize.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_GetADCEnableChannelSize.restype = ctypes.c_ubyte

        # 获取DAQ331M当前采样率
        self.daq331m_dll_.DAQ331M_GetCurrentSampleRate.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_GetCurrentSampleRate.restype = ctypes.c_uint32

        # 获取DAQ331M合法通道大小
        self.daq331m_dll_.DAQ331M_GetADCLegalChannelSize.argtypes = [ctypes.POINTER(ctypes.c_uint32)]
        self.daq331m_dll_.DAQ331M_GetADCLegalChannelSize.restype = ctypes.c_uint32

        # 判断当前所有通道缓冲区的数据量是否超过指定数值
        self.daq331m_dll_.DAQ331M_ADCDataIsReady.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint32]
        self.daq331m_dll_.DAQ331M_ADCDataIsReady.restype = ctypes.c_bool

    def create_device(self) -> None:
        self.daq331m_ = self.daq331m_dll_.DAQ331M_New()

    def delete_device(self) -> None:
        if self.daq331m_ is None:
            return
        self.daq331m_dll_.DAQ331M_Delete(self.daq331m_)

    def initialize_device(self) -> bool:
        if self.daq331m_ is None:
            return False
        else:
            result = self.daq331m_dll_.DAQ331M_InitializeDevice(self.daq331m_)

        return result

    def connected_device(self) -> bool:
        result = self.daq331m_dll_.DAQ331M_ConnectedDevice(self.daq331m_)

        return result

    def use_serial_backend(self, uart_name: str, baud_rate: int) -> bool:
        uart_name_bytes = uart_name.encode('ascii')

        result = self.daq331m_dll_.DAQ331M_UseSerialBackend(self.daq331m_, uart_name_bytes, baud_rate)

        return result

    def use_tcp_client_backend(self, ip_address: str, port: int) -> bool:
        ip_address_bytes = ip_address.encode('ascii')

        result = self.daq331m_dll_.DAQ331M_UseTCPClientBackend(self.daq331m_, ip_address_bytes, port)

        return result

    def use_modbus_backend(self, uart_name: str, baud_rate: int, address: int) -> bool:
        uart_name_bytes = uart_name.encode('ascii')

        result = self.daq331m_dll_.DAQ331M_UseModbusRTUBackend(self.daq331m_, uart_name_bytes, baud_rate)

        return result

    def configure_adc_parameters(self, voltage: int, sampling_rate: int) -> bool:
        result = self.daq331m_dll_.DAQ331M_ConfigureADCParameters(self.daq331m_, voltage, sampling_rate)

        return result

    def configure_adc_channel(self, channel: int) -> bool:
        result = self.daq331m_dll_.DAQ331M_ConfigADCChannel(self.daq331m_, ctypes.c_ubyte(channel))

        return result

    def start_collection(self) -> bool:
        result = self.daq331m_dll_.DAQ331M_StartCollection(self.daq331m_)

        return result

    def stop_collection(self) -> bool:
        result = self.daq331m_dll_.DAQ331M_StopCollection(self.daq331m_)

        return result

    def try_read_adc_data(self, channel: int, data: np.ndarray, read_size: int, timeout: int) -> bool:
        data_ptr = data.ctypes.data_as(ctypes.POINTER(ctypes.c_double))

        result = self.daq331m_dll_.DAQ331M_TryReadADCData(self.daq331m_, ctypes.c_uint(channel), data_ptr,
                                                          ctypes.c_uint(read_size), ctypes.c_uint(timeout))

        return result

    def get_adc_enable_channel_size(self) -> int:

        return self.daq331m_dll_.DAQ331M_GetADCEnableChannelSize(self.daq331m_)

    def get_current_sample_rate(self) -> int:

        return self.daq331m_dll_.DAQ331M_GetCurrentSampleRate(self.daq331m_)

    def get_adc_legal_channel_size(self) -> int:

        return self.daq331m_dll_.DAQ331M_GetADCLegalChannelSize(self.daq331m_)

    def adc_data_is_ready(self, data_num: int) -> bool:

        return self.daq331m_dll_.DAQ331M_ADCDataIsReady(self.daq331m_, data_num)
